import axios from 'axios';
import type { AxiosRequestConfig, AxiosResponse, CancelTokenStatic, Canceler } from 'axios';

import { message } from 'ant-design-vue';

const server = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_URL as string,
  timeout: 30000
});

const CancelToken: CancelTokenStatic = axios.CancelToken;

export type { Canceler };

const requestMap = new Map();

interface IRequestConfig extends AxiosRequestConfig {
  cancel?: undefined | ((c: Canceler) => void);
}

server.interceptors.request.use(
  (config: IRequestConfig) => {
    removeRequestMap(config);
    addRequestMap(config);
    return config;
  },
  (err) => Promise.reject(err)
);

server.interceptors.response.use(
  (res: AxiosResponse) => {
    // console.log(res);
    removeRequestMap(res.config);
    return res.data;
  },
  (err) => {
    removeRequestMap(err.config || {});
    return Promise.reject(err);
  }
);

function generateRequestKey(config: IRequestConfig) {
  const { url, method, params = {}, data = {} } = config;
  return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&');
}

/* 取消连续重复请求 */
function removeRequestMap(config: IRequestConfig) {
  if (requestMap.size === 0) return;
  const key = generateRequestKey(config);
  if (requestMap.has(key)) {
    const cancel = requestMap.get(key);
    cancel(key.split('&')[0] + ' 请求已被取消');
    requestMap.delete(key);
  }
}
function addRequestMap(config: IRequestConfig) {
  const key = generateRequestKey(config);
  config.cancelToken = new CancelToken((cancel) => {
    config.cancel && config.cancel(cancel);
    requestMap.set(key, cancel);
  });
}

interface IRequestConfigType extends IRequestConfig {
  method?: 'GET' | 'POST' | 'PATCH' | 'DELETE' | 'PUT';
}

interface IResponse<T = unknown> {
  code: number;
  msg: string;
  data: T;
}

export default async function request<T = unknown>(requestConfig: IRequestConfigType) {
  if (Object.prototype.toString.call(requestConfig) !== '[object Object]') {
    throw new TypeError('请求配置参数必须是一个对象');
  }
  let res;
  try {
    res = (await server({
      ...requestConfig,
      method: requestConfig.method || 'GET'
    })) as unknown as IResponse<T>;
  } catch (err) {
    const error = new Error(err as string);
    // console.log(error);
    error.message = error.message.replace(/Cancel:?/, '').trim();
    error.message && message.error(error.message);
  }
  return res as IResponse<T>;
}
